#include <iostream>
#include <cstring>
#include <fstream>

#include "XMLRawParser.h"
#include "Arguments.h"

const std::string version= "0.1.0";
// ----------------------------------------------------------------------------

namespace gpxtools
{
  class GPXRm : public XMLParserHandler
  {
  public:
    // -- Constructor -----------------------------------------------------------
    GPXRm() :
      _arguments("gpxrm [OPTION].. [FILE]\nRemove waypoint, track or route from GPX-file.\n", "gpxrm v0.1", "Remove waypoint, track or route from GPX-file and display the resulting file on standard output."),
      _waypoint(_arguments, true,  'w', "waypoint",    "NAME", "remove the waypoint with NAME", ""),
      _track   (_arguments, true,  't', "track",       "NAME", "remove the track with NAME", ""),
      _segment (_arguments, true,  's', "segment",     "NR",   "remove only segment in track with NR", ""),
      _route   (_arguments, true,  'r', "route",       "NAME", "remove the route with NAME", ""),
      _xmlParser(this),
      _inWaypoint(false),
      _inRoute(false),
      _inTrack(false),
      _inSegment(false),
      _segmentNr(0)
    {
    }

    // -- Deconstructor ---------------------------------------------------------
    virtual ~GPXRm()
    {
    }

    // -- Properties ------------------------------------------------------------

    // -- Parse arguments -------------------------------------------------------
    bool processArguments(int argc, char *argv[])
    {
      std::vector<std::string> filenames;

      if (!_arguments.parse(argc,argv, filenames))
      {
        return false;
      }
      else if (!checkArguments(filenames))
      {
        return false;
      }
      else if (filenames.empty())
      {
        return _xmlParser.parse(std::cin);
      }
      else
      {
        return parseFile(filenames.front());
      }
    }

    // -- Check arguments ---------------------------------------------------------
    bool checkArguments(const std::vector<std::string> &filenames)
    {
      if (filenames.size() > 1)
      {
        std::cerr << "Too many input files."  << std::endl;
        return false;
      }

      if (!_segment.value().empty())
      {
        try
        {
          _segmentNr = std::stoi(_segment.value());
        }
        catch (...)
        {
          std::cerr << "Invalid segment number: " << _segment.value() << std::endl;
          return false;
        }

        if (_track.value().empty())
        {
          std::cerr << "Missing track filter for segment number." << std::endl;
          return false;
        }
      }

      return true;
    }

    // -- Parse a file ----------------------------------------------------------
    bool parseFile(const std::string &filename)
    {
      bool ok =false;

      std::ifstream file(filename);

      if (file.is_open())
      {
        ok = _xmlParser.parse(file);

        file.close();
      }
      else
      {
        std::cerr << "Unable to open: " << filename << std::endl;
      }

      return ok;
    }

  private:
    void store(const std::string &text)
    {
      if (_inWaypoint || _inRoute || _inTrack || _inSegment)
      {
        _currentText.append(text);
      }
      else
      {
        std::cout << text;
      }
    }

  public:
    // -- Callbacks -------------------------------------------------------------
    virtual void unhandled(const std::string &, const std::string &text)
    {
      store(text);
    }

    virtual void startElement(const std::string &path, const std::string &text, const std::string &, const Attributes &)
    {
      if (path == "/gpx/wpt")
      {
        if (!_waypoint.value().empty())
        {
          _inWaypoint = true;
        }

        _currentText.clear();
        _currentName.clear();

        _currentSegmentNr = 0;
      }
      else if (path == "/gpx/rte")
      {
        if (!_route.value().empty())
        {
          _inRoute = true;
        }

        _currentText.clear();
        _currentName.clear();

        _currentSegmentNr = 0;
      }
      else if (path == "/gpx/trk")
      {
        if (!_track.value().empty() && (_segmentNr == 0))
        {
          _inTrack = true;
        }

        _currentText.clear();
        _currentName.clear();

        _currentSegmentNr = 0;
      }
      else if (path == "/gpx/trk/trkseg")
      {
        _currentSegmentNr++;

        if ((_segmentNr != 0) && (_segmentNr == _currentSegmentNr))
        {
          _currentText.clear();

          _inSegment = true;
        }
      }

      store(text);
    }

    virtual void text(const std::string &path, const std::string &text)
    {
      if (path == "/gpx/wpt/name" || path == "/gpx/rte/name" || path == "/gpx/trk/name")
      {
        _currentName.append(text);
      }

      store(text);
    }

    virtual void endElement(const std::string &path, const std::string &text, const std::string &)
    {
      store(text);

      XMLRawParser::trim(_currentName);

      if (path == "/gpx/wpt")
      {
        if (_inWaypoint && (_currentName != _waypoint.value()))
        {
          std::cout << _currentText;
        }

        _inWaypoint = false;
      }
      else if (path == "/gpx/rte")
      {
        if (_inRoute && (_currentName != _route.value()))
        {
          std::cout << _currentText;
        }

        _inRoute = false;
      }
      else if (path == "/gpx/trk")
      {
        if (_inTrack && (_currentName != _track.value()))
        {
          std::cout << _currentText;
        }

        _inTrack = false;
      }
      else if (path == "/gpx/trk/trkseg")
      {
        if (_inSegment && (_currentName != _track.value()))
        {
          std::cout << _currentText;
        }

        _inSegment = false;
      }
    }

  private:
    // Members
    arg::Arguments  _arguments;
    arg::Argument   _waypoint;
    arg::Argument   _track;
    arg::Argument   _segment;
    arg::Argument   _route;

    XMLRawParser    _xmlParser;

    bool          _inWaypoint;
    bool          _inRoute;
    bool          _inTrack;
    std::string   _currentText;
    std::string   _currentName;

    bool          _inSegment;
    int           _segmentNr;
    int           _currentSegmentNr;
  };
}

// -- Main program ------------------------------------------------------------

int main(int argc, char *argv[])
{
  gpxtools::GPXRm gpxRm;

  return gpxRm.processArguments(argc, argv) ? 0 : 1;
}
